package com.imooc.pan.server.modules.user.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.imooc.pan.cache.core.constants.CacheConstants;
import com.imooc.pan.core.constants.RPanConstants;
import com.imooc.pan.core.exception.RPanBusinessException;
import com.imooc.pan.core.response.ResponseCode;
import com.imooc.pan.core.utils.IdUtil;
import com.imooc.pan.core.utils.JwtUtil;
import com.imooc.pan.core.utils.PasswordUtil;
import com.imooc.pan.server.common.utils.UserIdUtil;
import com.imooc.pan.server.modules.file.constants.FileConstants;
import com.imooc.pan.server.modules.file.context.CreateFolderContext;
import com.imooc.pan.server.modules.file.entity.RPanUserFile;
import com.imooc.pan.server.modules.file.service.RPanFileService;
import com.imooc.pan.server.modules.file.service.RPanUserFileService;
import com.imooc.pan.server.modules.user.constants.UserConstants;
import com.imooc.pan.server.modules.user.context.*;
import com.imooc.pan.server.modules.user.converter.UserConverter;
import com.imooc.pan.server.modules.user.entity.RPanUser;
import com.imooc.pan.server.modules.user.service.RPanUserService;
import com.imooc.pan.server.modules.user.mapper.RPanUserMapper;
import com.imooc.pan.server.modules.user.vo.UserInfoVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Objects;

/**
* @author 黄怀镔
* @description 针对表【r_pan_user(用户信息表)】的数据库操作Service实现
* @createDate 2024-06-06 22:43:07
*/
@Service
public class RPanUserServiceImpl extends ServiceImpl<RPanUserMapper, RPanUser> implements RPanUserService{

    @Autowired
    private UserConverter userConverter;

    @Autowired
    private RPanUserFileService rPanUserFileService;

    @Autowired
    private CacheManager cacheManager;


    /**
     * 用户注册
     * @param userRegisterContext
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long register(UserRegisterContext userRegisterContext) {
        assembleUserEntity(userRegisterContext);
        doRegister(userRegisterContext);
        createRegisterRootFolder(userRegisterContext);
        return userRegisterContext.getEntity().getUserId();
    }

    /**
     * 用户登录
     * @param userLoginContext
     * @return
     */
    @Override
    public String login(UserLoginContext userLoginContext) {
        //校验用户
        checkUserInfo(userLoginContext);
        createAccessToken(userLoginContext);
        return userLoginContext.getAccessToken();
    }

    /**
     * 用户退出登录清除缓存
     * @param userId
     */
    @Override
    public void exit(Long userId) {
        try {
            Cache cache = cacheManager.getCache(CacheConstants.R_PAN_CACHE_NAME);
            cache.evict(UserConstants.USER_LOGIN_PREFIX+userId);
            UserIdUtil.remove();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RPanBusinessException("用户登出失败");
        }
    }

    @Override
    public String checkUserName(CheckUserNameContext checkUserNameContext) {
        String question = lambdaQuery().eq(RPanUser::getUsername, checkUserNameContext.getUsername()).one().getQuestion();
        if(StringUtils.isBlank(question)){
            throw new RPanBusinessException("用户不存在");
        }
        return question;
    }

    @Override
    public String checkAnswer(CheckAnswerContext checkAnswerContext) {
        Integer count = lambdaQuery().eq(RPanUser::getUsername, checkAnswerContext.getUsername())
                .eq(RPanUser::getQuestion, checkAnswerContext.getQuestion())
                .eq(RPanUser::getAnswer, checkAnswerContext.getAnswer()).count();
        if(count == 0){
            throw new RPanBusinessException("密保答案错误");
        }
        String token = generateAnswerToken(checkAnswerContext);
        return token;
    }

    /**
     * 生成用户忘记密码-校验密保答案通过的临时token
     * @param checkAnswerContext
     * @return
     */
    private String generateAnswerToken(CheckAnswerContext checkAnswerContext) {
        String token = JwtUtil.generateToken(checkAnswerContext.getUsername(), UserConstants.FORGET_PASSWORD_USERNAME, checkAnswerContext.getUsername(), UserConstants.FIVE_MINUTES_LONG);
        return token;
    }

    /**
     * 修改密码
     * @param resetPasswordContext
     * @return
     */
    @Override
    public void resetPassword(ResetPasswordContext resetPasswordContext) {
        checkForgetPasswordToken(resetPasswordContext);
        checkAndUpdateUser(resetPasswordContext);
    }

    /**
     * 修改用户密码
     * @param changePasswordContext
     */
    @Override
    public void changePassword(ChangePasswordContext changePasswordContext) {
        checkOldPassword(changePasswordContext);
        doChangePassword(changePasswordContext);
        exitLoginStatus(changePasswordContext);
    }

    /**
     * 查询用户基本信息
     * @param userId
     * @return
     */
    @Override
    public UserInfoVo info(Long userId) {
        RPanUser entity = getById(userId);
        if(Objects.isNull(entity)){
            throw new RPanBusinessException("用户信息查询失败");
        }
        RPanUserFile rPanUserFile = getUserRootFile(userId);
        if(Objects.isNull(rPanUserFile)){
            throw new RPanBusinessException("文件信息查询失败");
        }
        return userConverter.assembleUserInfoVO(entity,rPanUserFile);
    }

    /**
     * 获取用户根文件夹信息
     * @param userId
     * @return
     */
    private RPanUserFile getUserRootFile(Long userId) {
        return rPanUserFileService.getUserRootFile(userId);
    }

    /**
     * 退出登录
     * @param changePasswordContext
     */
    private void exitLoginStatus(ChangePasswordContext changePasswordContext) {
        exit(changePasswordContext.getUserId());
    }

    /**
     * 更改密码操作
     * @param changePasswordContext
     */
    private void doChangePassword(ChangePasswordContext changePasswordContext) {
        RPanUser entity = changePasswordContext.getEntity();
        String newPassword = changePasswordContext.getNewPassword();
        String salt = entity.getSalt();
        String newDbPassword = PasswordUtil.encryptPassword(salt,newPassword);
        entity.setPassword(newDbPassword);
        if(!updateById(entity)){
            throw new RPanBusinessException("用户修改密码失败");
        }
    }

    /**
     * 检查旧密码是否相同
     * @param changePasswordContext
     */
    private void checkOldPassword(ChangePasswordContext changePasswordContext) {
        String oldPassword = changePasswordContext.getPassword();
        Long userId = changePasswordContext.getUserId();
        RPanUser entity = lambdaQuery().eq(RPanUser::getUserId, userId).one();
        if(Objects.isNull(entity)){
            throw new RPanBusinessException("用户信息不存在");
        }
        changePasswordContext.setEntity(entity);
        String salt = entity.getSalt();
        String password = PasswordUtil.encryptPassword(salt,oldPassword);
        if(!Objects.equals(password,entity.getPassword())){
            throw new RPanBusinessException("旧密码不正确");
        }
    }

    /**
     * 检查并修改用户
     * @param resetPasswordContext
     */
    private void checkAndUpdateUser(ResetPasswordContext resetPasswordContext) {
        String username = resetPasswordContext.getUsername();
        String password = resetPasswordContext.getPassword();
        RPanUser entity = lambdaQuery().eq(RPanUser::getUsername, username).one();
        if(Objects.isNull(entity)){
            throw new RPanBusinessException("用户不存在");
        }
        String salt = entity.getSalt();
        password = PasswordUtil.encryptPassword(salt,password);
        entity.setUpdateTime(new Date());
        entity.setPassword(password);
        if(!updateById(entity)){
            throw new RPanBusinessException("修改用户密码失败");
        }
    }

    /**
     * 校验密保答案是否正确
     * @param resetPasswordContext
     */
    private void checkForgetPasswordToken(ResetPasswordContext resetPasswordContext) {
        String token = resetPasswordContext.getToken();
        Object value = JwtUtil.analyzeToken(token, UserConstants.FORGET_PASSWORD_USERNAME);
        if(Objects.isNull(value)){
            throw new RPanBusinessException(ResponseCode.TOKEN_EXPIRE);
        }
        String tokenUserName = String.valueOf(value);
        if(!Objects.equals(tokenUserName,resetPasswordContext.getUsername())){
            throw new RPanBusinessException("token错误");
        }
    }



    /**
     * 校验用户
     * @param userLoginContext
     */
    private void checkUserInfo(UserLoginContext userLoginContext) {
        String username = userLoginContext.getUsername();
        String password = userLoginContext.getPassword();
        RPanUser user = lambdaQuery().eq(RPanUser::getUsername, username).one();
        if(Objects.isNull(user)){
            throw new RPanBusinessException("用户不存在");
        }
        String passwordDB = user.getPassword();
        String salt = user.getSalt();
        password = PasswordUtil.encryptPassword(salt, password);
        if(!Objects.equals(passwordDB,password)){
            throw new RPanBusinessException("用户名或密码错误");
        }
        userLoginContext.setEntity(user);
    }
    /**
     * 生成accessToken
     * @param userLoginContext
     */
    private void createAccessToken(UserLoginContext userLoginContext) {
        RPanUser entity = userLoginContext.getEntity();
        String accessToken = JwtUtil.generateToken(entity.getUsername(), UserConstants.LOGIN_USER_ID, entity.getUserId(), UserConstants.FIVE_MINUTES_LONG);
        userLoginContext.setAccessToken(accessToken);
        Cache cache = cacheManager.getCache(CacheConstants.R_PAN_CACHE_NAME);
        //设置缓存
        cache.put(UserConstants.USER_LOGIN_PREFIX+entity.getUserId(),accessToken);
        //设置用户id
        UserIdUtil.set(entity.getUserId());
    }

    /**
     * 创建用户根目录信息
     * @param userRegisterContext
     */
    private void createRegisterRootFolder(UserRegisterContext userRegisterContext) {
        CreateFolderContext createFolderContext = new CreateFolderContext();
        createFolderContext.setParentId(FileConstants.TOP_PARENT_ID);
        createFolderContext.setUserId(userRegisterContext.getEntity().getUserId());
        createFolderContext.setFolderName(FileConstants.ALL_FILE_CN_STR);
        rPanUserFileService.createFolder(createFolderContext);
    }

    /**
     * 用户注册
     * @param userRegisterContext
     */
    private void doRegister(UserRegisterContext userRegisterContext) {
        RPanUser user = userRegisterContext.getEntity();
        if(Objects.nonNull(user)){
            try {
                if(!save(user)){
                    throw new RPanBusinessException("用户注册失败");
                }
            } catch (DuplicateKeyException duplicateKeyException) {
                throw new RPanBusinessException("用户名已存在");
            }
        }else{
            throw new RPanBusinessException(ResponseCode.ERROR);
        }

    }

    /**
     * 设置RPanUser
     * @param userRegisterContext
     */
    private void assembleUserEntity(UserRegisterContext userRegisterContext) {
        RPanUser entity = userConverter.userRegisterContextToRPanUser(userRegisterContext);
        //生成盐值
        String salt = PasswordUtil.getSalt();
        //密码加密
        String password = PasswordUtil.encryptPassword(salt,userRegisterContext.getPassword());
        entity.setUserId(IdUtil.get());
        entity.setPassword(password);
        entity.setSalt(salt);
        entity.setCreateTime(new Date());
        entity.setUpdateTime(new Date());
        userRegisterContext.setEntity(entity);
    }
}
